class Compiler {
  constructor(vm) {
    this.el = vm.$el
    this.vm = vm
    this.compiler(this.el)
  }
  // 编译模板，处理文本节点和元素节点
  compiler(el) {
    let childNodes = el.childNodes
    Array.from(childNodes).forEach(node => {
      // 处理文本节点
      if(this.isTextNode(node)){
        this.compilerText(node)
      }else if(this.isElementNode(node)){
        this.compilerElement(node)
      }
      // 判断是否有子节点
      if(node.childNodes && node.childNodes.length){
        this.compiler(node)
      }
    })
  }

  compilerElement(node) {
    Array.from(node.attributes).forEach(attr => {
      let attrName = attr.name
      if(this.isDirective(attrName)){
        attrName = attrName.substr(2)
        let key = attr.value
        if(attrName.indexOf(':') !== -1){
          this.onUpdater(node,this.vm[key],attrName)
        }else{
          this.update(node, key, attrName)
        }
      }
    })
  }

  update(node, key, attrName){
    let updateFn = this[attrName + 'Updater']
    updateFn && updateFn.call(this, node, this.vm[key], key)
  }

  textUpdater(node, value, key) {
    node.textContent = value
    new Watcher(this.vm, key, (v) => {
      node.textContent = v
    })
  }
  onUpdater(node,value,attrName){
    let type = attrName.split(':')[1]
    node.addEventListener(type,value)
  }
  htmlUpdater(node,value,key) {
    node.innerHTML = value
    new Watcher(this.vm, key, (v) => {
      node.innerHTML = v
    })
  }
  modelUpdater(node, value, key) {
    node.value = value
    new Watcher(this.vm, key, (v) => {
      node.value = v
    })
    // 双向绑定
    node.addEventListener('input', () => {
      this.vm[key] = node.value
    })
  }

  compilerText(node) {
    // {{ msg }}
    let reg = /\{\{(.+?)\}\}/
    let value = node.textContent
    if(reg.test(value)){
      let key = RegExp.$1.trim()
      node.textContent = value.replace(reg,this.vm[key])
      // 创建watcher
      new Watcher(this.vm, key, (newValue) => {
        node.textContent = newValue
      })
    }
  }

  isDirective(attrName) {
    return attrName.startsWith('v-')
  }

  isTextNode(node) {
    return node.nodeType === 3
  }

  isElementNode(node) {
    return node.nodeType === 1
  }
}